package com.jplus.framework.core.classscan;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jplus.framework.util.ClassUtil;
import com.jplus.framework.util.FormatUtil;

/**
 * 用于获取类的模板类
 *
 * @author huangyong
 * @author Yuanqy
 */
public abstract class ClassTemplate<T> {

	private static final Logger logger = LoggerFactory.getLogger(ClassTemplate.class);

	protected final String packageName;

	protected ClassTemplate(String packageName) {
		this.packageName = packageName;
	}

	public final List<T> getList() {
		List<T> classList = new ArrayList<T>();
		try {
			// 从包名获取 URL 类型的资源
			Enumeration<URL> urls = ClassUtil.getClassLoader().getResources(packageName.replace(".", "/"));
			// 遍历 URL 资源
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					// 获取协议名（分为 file 与 jar）
					String protocol = url.getProtocol();
					if (protocol.equals("file")) {
						// 若在 class 目录中，则执行添加类操作
						String packagePath = url.getPath().replaceAll("%20", " ");
						addClass(classList, packagePath, packageName);
					} else if (protocol.equals("jar")) {
						// 若在 jar 包中，则解析 jar 包中的 entry
						JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
						JarFile jarFile = jarURLConnection.getJarFile();
						Enumeration<JarEntry> jarEntries = jarFile.entries();
						while (jarEntries.hasMoreElements()) {
							JarEntry jarEntry = jarEntries.nextElement();
							String file = jarEntry.getName();
							if (file.contains(".")  && !file.endsWith("/") && !file.endsWith(".") && !file.endsWith(File.separator)) {
								logger.debug(file);
								String suffix = file.substring(file.lastIndexOf("."));
								String fileName = file.substring(file.lastIndexOf("/") + 1, file.length() - suffix.length());

								String filePkg = file.indexOf("/")>0?file.substring(0, file.length() - fileName.length() - suffix.length() - 1).replaceAll("/", "."):"";
								// 执行添加类操作
								doAddClass(classList, filePkg, fileName, suffix);
							}
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取类出错！", e);
		}
		return classList;
	}

	private void addClass(List<T> classList, String packagePath, String packageName) {
		try {
			// 获取包名路径下的 文件或目录
			File[] files = new File(packagePath).listFiles(new FileFilter() {
				@Override
				public boolean accept(File pathname) {
					return pathname.isDirectory() || pathname.getName().indexOf(".") > 0;
				}
			});
			// 遍历文件或目录
			for (File file : files) {
				String fileName = file.getName();
				// 判断是否为文件或目录
				if (file.isFile()) {
					// 获取类名
					String className = fileName.substring(0, fileName.lastIndexOf("."));
					// 执行添加类操作
					doAddClass(classList, packageName, className, fileName.substring(fileName.lastIndexOf("."), fileName.length()));
				} else {
					// 获取子包
					String subPackagePath = fileName;
					if (!FormatUtil.isEmpty(packagePath)) {
						subPackagePath = packagePath + "/" + subPackagePath;
					}
					// 子包名
					String subPackageName = fileName;
					if (!FormatUtil.isEmpty(packageName)) {
						subPackageName = packageName + "." + subPackageName;
					}
					// 递归调用
					addClass(classList, subPackagePath, subPackageName);
				}
			}
		} catch (Exception e) {
			logger.error("添加类出错！", e);
		}
	}

	private void doAddClass(List<T> filelist, String packageName, String fileName, String suffix) {
		// 判断是否可以添加类
		checkAndAddClass(packageName, fileName, suffix, filelist);
	}

	/**
	 * 验证是否允许添加类
	 */
	public abstract void checkAndAddClass(String packageName, String fileName, String suffix, List<T> filelist);

	public static void main(String[] args) {
		String file = "com/jplus/framework/aop/annotation/Aspect.class";
		String suffix = file.substring(file.lastIndexOf("."));
		String fileName = file.substring(file.lastIndexOf("/") + 1, file.length() - suffix.length());
		String filePkg = file.substring(0, file.length() - fileName.length() - suffix.length() - 1);
		System.out.println("suffix=" + suffix);
		System.out.println("fileName=" + fileName);
		System.out.println("filePkg=" + filePkg);
	}
}